package com.lwy.setter;

import lombok.extern.slf4j.Slf4j;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 名称填充器基类，用于View层传输对象的名称填充
 *
 * @param <A> 目标注解类型
 * @author bukong
 * @date 2022/6/25
 */
@Slf4j
public abstract class BaseNameSetter<A extends Annotation> {

    public BaseNameSetter() {
        NameSetterMeta.registerNameSetter(getAnnotationClz(), this);
    }

    public abstract boolean isFillNameByCode();

    /**
     * 适用于通过id（Long）填充name的场景，必须重写 getIdNameMap 方法
     *
     * @param beans    待填充对象列表
     * @param beanType 待填充对象类型
     * @param <T>      待填充对象类型
     */
    public <T> void fillNameById(List<T> beans, Class<T> beanType) {
        try {
            final Map<Field, Field> fieldMapper = NameSetterMeta.getFieldMapper(beanType, getAnnotationClz());
            Set<Long> ids = new HashSet<>();
            for (Field field : fieldMapper.keySet()) {
                for (T bean : beans) {
                    final Object id = field.get(bean);
                    if (id == null) {
                        continue;
                    } else if (id instanceof Number) {
                        ids.add(Long.valueOf(id.toString()));
                    } else if (id instanceof Collection) {
                        List<Long> beanIds = ((Collection<?>) id).stream().map(e -> Long.valueOf(e.toString())).collect(Collectors.toList());
                        ids.addAll(beanIds);
                    }
                }
            }
            final Map<Long, String> idNameMap = getIdNameMap(new ArrayList<>(ids));
            for (Map.Entry<Field, Field> entry : fieldMapper.entrySet()) {
                final Field idField = entry.getKey();
                final Field nameField = entry.getValue();
                for (T bean : beans) {
                    final Object id = idField.get(bean);
                    if (id == null) {
                        continue;
                    } else if (id instanceof Number) {
                        final Long aLong = Long.valueOf(id.toString());
                        if (idNameMap.containsKey(aLong)) {
                            nameField.set(bean, idNameMap.get(aLong));
                        }
                    } else if (id instanceof Collection) {
                        List<Long> beanIds = ((Collection<?>) id).stream().map(e -> Long.valueOf(e.toString())).collect(Collectors.toList());
                        List<String> nameList = beanIds.stream().map(idNameMap::get).collect(Collectors.toList());
                        nameField.set(bean, nameList);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            // ignore
        } catch (Exception e) {
            log.error("function fillNameById error: " + e.getMessage(), e);
        }
    }

    /**
     * 适用于通过code（String）填充name的场景，必须重写 getCodeNameMap 方法
     *
     * @param beans    待填充对象列表
     * @param beanType 待填充对象类型
     * @param <T>      待填充对象类型
     */
    public <T> void fillNameByCode(List<T> beans, Class<T> beanType) {
        try {
            final Map<Field, Field> fieldMapper = NameSetterMeta.getFieldMapper(beanType, getAnnotationClz());
            // 用set去重
            Set<String> codes = new HashSet<>();
            for (Field field : fieldMapper.keySet()) {
                for (T bean : beans) {
                    final Object code = field.get(bean);
                    if (code == null) {
                        continue;
                    } else if (code instanceof CharSequence) {
                        codes.add(code.toString());
                    } else if (code instanceof Collection) {
                        codes.addAll(new ArrayList<>((Collection<String>) code));
                    }
                }
            }
            final Map<String, String> codeNameMap = getCodeNameMap(new ArrayList<>(codes));
            for (Map.Entry<Field, Field> entry : fieldMapper.entrySet()) {
                final Field codeField = entry.getKey();
                final Field nameField = entry.getValue();
                for (T bean : beans) {
                    final Object code = codeField.get(bean);
                    if (code == null) {
                        continue;
                    } else if (code instanceof CharSequence) {
                        nameField.set(bean, codeNameMap.get(code.toString()));
                    } else if (code instanceof Collection) {
                        ArrayList<String> beanCodes = new ArrayList<>((Collection<String>) code);
                        List<String> names = beanCodes.stream().map(codeNameMap::get).collect(Collectors.toList());
                        nameField.set(bean, names);
                    }
                }
            }
        } catch (IllegalAccessException e) {
            // ignore
        } catch (Exception e) {
            log.error("function fillNameByCode error: " + e.getMessage(), e);
        }
    }

    protected Map<String, String> getCodeNameMap(List<String> codes) {
        return Collections.emptyMap();
    }

    protected abstract Class<A> getAnnotationClz();

    protected Map<Long, String> getIdNameMap(List<Long> ids) {
        return Collections.emptyMap();
    }
}
